home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / Astro / ephem / Source / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  25.5 KB  |  1,040 lines

  1. /* main "ephem" program. 
  2.  * -------------------------------------------------------------------
  3.  * Copyright (c) 1990,1991,1992 by Elwood Charles Downey
  4.  * 
  5.  * Permission is granted to make and distribute copies of this program
  6.  * free of charge, provided the copyright notice and this permission
  7.  * notice are preserved on all copies.  All other rights reserved.
  8.  * -------------------------------------------------------------------
  9.  * set options.
  10.  * init screen and circumstances.
  11.  * enter infinite loop updating screen and allowing operator input.
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <ctype.h>
  16. #include <signal.h>
  17. #include <setjmp.h>
  18. #include <math.h>
  19. #ifdef VMS
  20. #include <stdlib.h>
  21. #endif
  22. #ifdef NeXT
  23. # include <stdlib.h>
  24. #endif
  25. #include "astro.h"
  26. #include "circum.h"
  27. #include "screen.h"
  28.  
  29. extern char *strncpy();
  30. extern char *getenv();
  31.  
  32. /* shorthands for fields of a Now structure, now.
  33.  * first undo the ones for a Now pointer from circum.h.
  34.  */
  35. #undef mjd
  36. #undef lat
  37. #undef lng
  38. #undef tz
  39. #undef temp
  40. #undef pressure
  41. #undef height
  42. #undef epoch
  43. #undef tznm
  44.  
  45. #define mjd    now.n_mjd
  46. #define lat    now.n_lat
  47. #define lng    now.n_lng
  48. #define tz    now.n_tz
  49. #define temp    now.n_temp
  50. #define pressure now.n_pressure
  51. #define height    now.n_height
  52. #define epoch    now.n_epoch
  53. #define tznm    now.n_tznm
  54.  
  55. static jmp_buf fpe_err_jmp;    /* used to recover from SIGFPE */
  56. static char *cfgfile;        /* !0 if -c used */
  57. static char cfgdef[] = "ephem.cfg"; /* default configuration file name */
  58. static Now now;        /* where when and how, right now */
  59. static double tminc;    /* hrs to inc time by each loop; RTC means use clock */
  60. static int nstep;    /* steps to go before stopping */
  61. static int spause;    /* secs to pause between steps */
  62. static int optwi;    /* set when want to display dawn/dusk/len-of-night */
  63. static int oppl;    /* mask of (1<<planet) bits; set when want to show it */
  64.  
  65. main (ac, av)
  66. int ac;
  67. char *av[];
  68. {
  69.     void bye();
  70.     void on_fpe();
  71.     static char freerun[] =
  72.         "Running... press any key to stop to make changes.";
  73.     static char prmpt[] =
  74. "Move to another field, RETURN to change this field, ? for help, or q to run";
  75.     static char hlp[] =
  76.     "arrow keys move to field; any key stops running; ^d exits; ^l redraws";
  77.     int fld = rcfpack(R_NSTEP, C_NSTEPV, 0); /* initial cursor loc */
  78.     int sflag = 0;    /* not silent, by default */
  79.     int one = 1;    /* use a variable so optimizer doesn't get disabled */
  80.     int srchdone = 0; /* true when search funcs say so */
  81.     int newcir = 2;    /* set when circumstances change - means don't tminc */
  82.  
  83.     while ((--ac > 0) && (**++av == '-')) {
  84.         char *s;
  85.         for (s = *av+1; *s != '\0'; s++)
  86.         switch (*s) {
  87.         case 's': /* no credits "silent" (don't publish this) */
  88.             sflag++;
  89.             break;
  90.         case 'c': /* set name of config file to use */
  91.             if (--ac <= 0) usage("-c but no config file");
  92.             cfgfile = *++av;
  93.             break;
  94.         case 'd': /* set alternate database file name */
  95.             if (--ac <= 0) usage("-d but no database file");
  96.             obj_setdbfilename (*++av);
  97.             break;
  98.         default:
  99.             usage("Bad - option");
  100.         }
  101.     }
  102.  
  103.     if (!sflag)
  104.         credits();
  105.  
  106.     /* fresh screen.
  107.      * crack config file, THEN args so args may override.
  108.      */
  109.     c_erase();
  110.     read_cfgfile ();
  111.     read_fieldargs (ac, av);
  112.  
  113.     /* set up to clean up screen and tty if interrupted.
  114.      * also set up to stop if get floating error.
  115.      */
  116.     (void) signal (SIGINT, bye);
  117.     (void) signal (SIGFPE, on_fpe);
  118.  
  119.     /* update screen forever (until QUIT) */
  120.     while (one) {
  121.  
  122.         /* if get a floating error, longjmp() here and stop looping */
  123.         if (setjmp (fpe_err_jmp))
  124.         nstep = 0;
  125.         else {
  126.         nstep -= 1;
  127.  
  128.         /* recalculate everything and update all the fields */
  129.         redraw_screen (newcir);
  130.         mm_newcir (0);
  131.  
  132.         /* let searching functions change tminc and check for done */
  133.         srchdone = srch_eval (mjd, &tminc) < 0;
  134.         print_tminc(0);    /* to show possibly new search increment */
  135.  
  136.         /* update plot and listing files, now that all fields are up
  137.          * to date and search function has been evaluated.
  138.          */
  139.         plot();
  140.         listing();
  141.  
  142.         /* handle spause if we are really looping */
  143.         if (nstep > 0)
  144.             slp_sync();
  145.         }
  146.  
  147.         /* stop loop to allow op to change parameters:
  148.          * if a search evaluation converges (or errors out),
  149.          * or if steps are done,
  150.          * or if op hits any key.
  151.          */
  152.         newcir = 0;
  153.         if (srchdone || nstep <= 0 || (chk_char()==0 && read_char()!=0)) {
  154.         int nfld;
  155.  
  156.         /* update screen with the current stuff if stopped during
  157.          * unattended plotting or listing since last redraw_screen()
  158.          * didn't.
  159.          */
  160.         if ((plot_ison() || listing_ison()) && nstep > 0)
  161.             redraw_screen (1);
  162.  
  163.         /* return nstep to default of 1 */
  164.         if (nstep <= 0) {
  165.             nstep = 1;
  166.             print_nstep (0);
  167.         }
  168.  
  169.         /* change fields until END.
  170.          * update all time fields if any are changed
  171.          * and print NEW CIRCUMSTANCES if any have changed.
  172.          * QUIT causes bye() to be called and we never return.
  173.          */
  174.         while(nfld = sel_fld(fld,alt_menumask()|F_CHG,prmpt,hlp)) {
  175.             if (chg_fld ((char *)0, &nfld)) {
  176.             mm_now (&now, 1);
  177.             mm_newcir(1);
  178.             newcir = 1;
  179.             }
  180.             fld = nfld;
  181.         }
  182.         if (nstep > 1)
  183.             f_prompt (freerun);
  184.         }
  185.  
  186.         /* increment time only if op didn't change cirumstances */
  187.         if (!newcir)
  188.         inc_mjd (&now, tminc);
  189.     }
  190.  
  191.     return (0);
  192. }
  193.  
  194. /* read in ephem's configuration file, if any.
  195.  * if errors in file, call usage() (which exits).
  196.  * if use -d, require it; else try $EPHEMCFG and ephem.cfg but don't
  197.  *   complain if can't find these since, after all, one is not required.
  198.  * skip all lines that doesn't begin with an alpha char.
  199.  */
  200. static
  201. read_cfgfile()
  202. {
  203.     char buf[128];
  204.     FILE *fp;
  205.     char *fn;
  206.  
  207.     /* open the config file. 
  208.      * only REQUIRED if used -d option.
  209.      * if succcessful, fn points to file name.
  210.      */
  211.     if (cfgfile) {
  212.         fn = cfgfile;
  213.         fp = fopen (fn, "r");
  214.         if (!fp) {
  215.         (void) sprintf (buf, "Can not open %s", fn);
  216.         usage (buf);    /* does not return */
  217.         }
  218.     } else {
  219.         fn = getenv ("EPHEMCFG");
  220.         if (!fn)
  221.         fn = cfgdef;
  222.     }
  223.     fp = fopen (fn, "r");
  224.     if (!fp)
  225.         return;    /* oh well; after all, it's not required */
  226.  
  227.     while (fgets (buf, sizeof(buf), fp)) {
  228.         if (!isalpha(buf[0]))
  229.         continue;
  230.         buf[strlen(buf)-1] = '\0';        /* discard trailing \n */
  231.         if (crack_fieldset (buf) < 0) {
  232.         char why[NC];
  233.         (void) sprintf (why, "Bad field spec in %s: %s\n", fn, buf);
  234.         usage (why);
  235.         }
  236.     }
  237.     (void) fclose (fp);
  238. }
  239.  
  240.  
  241. /* draw all the stuff on the screen, using the current menu.
  242.  * if how_much == 0 then just update fields that need it;
  243.  * if how_much == 1 then redraw all fields;
  244.  * if how_much == 2 then erase the screen and redraw EVERYTHING.
  245.  */
  246. redraw_screen (how_much)
  247. int how_much;
  248. {
  249.     if (how_much == 2)
  250.         c_erase();
  251.  
  252.     /* print the single-step message if this is the last loop */
  253.     if (nstep < 1)
  254.         print_updating();
  255.  
  256.     if (how_much == 2) {
  257.         mm_borders();
  258.         mm_labels();
  259.         srch_prstate(1);
  260.         plot_prstate(1);
  261.         listing_prstate(1);
  262.     }
  263.  
  264.     /* if just updating changed fields while plotting or listing
  265.      * unattended then suppress most screen updates except
  266.      * always show nstep to show plot loops to go and
  267.      * always show tminc to show search convergence progress.
  268.      */
  269.     print_nstep(how_much);
  270.     print_tminc(how_much);
  271.     print_spause(how_much);
  272.     if (how_much == 0 && (plot_ison() || listing_ison()) && nstep > 0)
  273.         f_off();
  274.  
  275.     /* print all the time-related fields */
  276.     mm_now (&now, how_much);
  277.  
  278.     if (optwi)
  279.         mm_twilight (&now, how_much);
  280.  
  281.     /* print stuff on bottom menu */
  282.     print_alt (how_much);
  283.  
  284.     f_on();
  285. }
  286.  
  287. /* clean up and exit.
  288.  */
  289. void
  290. bye()
  291. {
  292.     c_erase();
  293.     byetty();
  294.     exit (0);
  295. }
  296.  
  297. /* this gets called when a floating point error occurs.
  298.  * we force a jump back into main() with looping terminated.
  299.  */
  300. static
  301. void
  302. on_fpe()
  303. {
  304.     extern void longjmp();
  305.  
  306.     (void) signal (SIGFPE, on_fpe);
  307.     f_msg ("Floating point error has occurred - computations aborted.");
  308.     longjmp (fpe_err_jmp, 1);
  309. }
  310.  
  311. usage(why)
  312. char *why;
  313. {
  314.     /* don't advertise -s (silent) option */
  315.     c_erase();
  316.     f_string (1, 1, why);
  317.     f_string (2, 1,
  318.         "usage: [-c <configfile>] [-d <database>] [field=value ...]\r\n");
  319.     byetty();
  320.     exit (1);
  321.  
  322. /* Process the field specs from the command line.
  323.  * if trouble call usage() (which exits).
  324.  */
  325. static
  326. read_fieldargs (ac, av)
  327. int ac;        /* number of such specs */
  328. char *av[];    /* array of strings in form <field_name value> */
  329. {
  330.     while (--ac >= 0) {
  331.         char *fs = *av++;
  332.         if (crack_fieldset (fs) < 0) {
  333.         char why[NC];
  334.         (void) sprintf (why, "Bad command line spec: %.*s",
  335.                             sizeof(why)-26, fs);
  336.         usage (why);
  337.         }
  338.     }
  339. }
  340.  
  341. /* process a field spec in buf, either from config file or argv.
  342.  * return 0 if recognized ok, else -1.
  343.  */
  344. static
  345. crack_fieldset (buf)
  346. char *buf;
  347. {
  348. #define    ARRAY_SIZ(a)    (sizeof(a)/sizeof((a)[0]))
  349. #define    MAXKW        6    /* longest keyword, not counting trailing 0 */
  350.     /* N.B. index of item is its case value, below.
  351.      * N.B. if add an item, keep it no longer than MAXKW chars.
  352.      */
  353.     static char keywords[][MAXKW+1] = {
  354.         /*  0 */    "LAT",
  355.         /*  1 */    "LONG",
  356.         /*  2 */    "UT",
  357.         /*  3 */    "UD",
  358.         /*  4 */    "TZONE",
  359.         /*  5 */    "TZNAME",
  360.         /*  6 */    "HEIGHT",
  361.         /*  7 */    "NSTEP",
  362.         /*  8 */    "PAUSE",
  363.         /*  9 */    "STPSZ",
  364.         /* 10 */    "TEMP",
  365.         /* 11 */    "PRES",
  366.         /* 12 */    "EPOCH",
  367.         /* 13 */    "JD",
  368.         /* 14 */    "OBJX",
  369.         /* 15 */    "OBJY",
  370.         /* 16 */    "PROPTS",
  371.         /* 17 */    "MENU"
  372.     };
  373.     int i;
  374.     int l;
  375.     int f;
  376.  
  377.     for (i = 0; i < ARRAY_SIZ(keywords); i++)
  378.         if (strncmp (keywords[i], buf, l = strlen(keywords[i])) == 0) {
  379.         buf += l+1;    /* skip keyword and its subsequent delimiter */
  380.         break;
  381.         }
  382.  
  383.     switch (i) {
  384.     case 0: f = rcfpack (R_LAT,C_LATV,0); (void) chg_fld (buf, &f);
  385.         break;
  386.     case 1: f = rcfpack (R_LONG,C_LONGV,0), (void) chg_fld (buf, &f);
  387.         break;
  388.     case 2: f = rcfpack (R_UT,C_UTV,0), (void) chg_fld (buf, &f);
  389.         break;
  390.     case 3: f = rcfpack (R_UD,C_UD,0), (void) chg_fld (buf, &f);
  391.         break;
  392.     case 4: f = rcfpack (R_TZONE,C_TZONEV,0), (void) chg_fld (buf, &f);
  393.         break;
  394.     case 5: f = rcfpack (R_TZN,C_TZN,0), (void) chg_fld (buf, &f);
  395.         break;
  396.     case 6: f = rcfpack (R_HEIGHT,C_HEIGHTV,0), (void) chg_fld (buf, &f);
  397.         break;
  398.     case 7: f = rcfpack (R_NSTEP,C_NSTEPV,0), (void) chg_fld (buf, &f);
  399.         break;
  400.     case 8: f = rcfpack (R_PAUSE,C_PAUSEV,0), (void) chg_fld (buf, &f);
  401.         break;
  402.     case 9: f = rcfpack (R_STPSZ,C_STPSZV,0), (void) chg_fld (buf, &f);
  403.         break;
  404.     case 10: f = rcfpack (R_TEMP,C_TEMPV,0), (void) chg_fld (buf, &f);
  405.         break;
  406.     case 11: f = rcfpack (R_PRES,C_PRESV,0), (void) chg_fld (buf, &f);
  407.         break;
  408.     case 12: f = rcfpack (R_EPOCH,C_EPOCHV,0), (void) chg_fld (buf, &f);
  409.         break;
  410.     case 13: f = rcfpack (R_JD,C_JDV,0), (void) chg_fld (buf, &f);
  411.         break;
  412.     case 14: (void) obj_filelookup (OBJX, buf);
  413.         break;
  414.     case 15: (void) obj_filelookup (OBJY, buf);
  415.         break;
  416.     case 16:
  417.         if (buf[-1] != '+')
  418.         optwi = oppl = 0;
  419.         while (*buf)
  420.         switch (*buf++) {
  421.         case 'T': optwi = 1; break;
  422.         case 'S': oppl |= (1<<SUN); break;
  423.         case 'M': oppl |= (1<<MOON); break;
  424.         case 'e': oppl |= (1<<MERCURY); break;
  425.         case 'v': oppl |= (1<<VENUS); break;
  426.         case 'm': oppl |= (1<<MARS); break;
  427.         case 'j': case 'J': oppl |= (1<<JUPITER); break;
  428.         case 's': oppl |= (1<<SATURN); break;
  429.         case 'u': oppl |= (1<<URANUS); break;
  430.         case 'n': oppl |= (1<<NEPTUNE); break;
  431.         case 'p': oppl |= (1<<PLUTO); break;
  432.         case 'x': oppl |= (1<<OBJX); obj_on(OBJX); break;
  433.         case 'y': oppl |= (1<<OBJY); obj_on(OBJY); break;
  434.         }
  435.         break;
  436.     case 17:
  437.         if (strncmp (buf, "DATA", 4) == 0)
  438.         altmenu_init (F_MNU1);
  439.         else if (strncmp (buf, "RISET", 5) == 0)
  440.         altmenu_init (F_MNU2);
  441.         else if (strncmp (buf, "SEP", 3) == 0)
  442.         altmenu_init (F_MNU3);
  443.         else if (strncmp (buf, "JUP", 3) == 0)
  444.         altmenu_init (F_MNUJ);
  445.         break;
  446.     default:
  447.         return (-1);
  448.     }
  449.     return (0);
  450. }
  451.  
  452. /* react to the field at *fld according to the optional string input at bp.
  453.  * if bp is != 0 use it, else issue read_line() and use buffer.
  454.  * then sscanf the buffer and update the corresponding (global) variable(s)
  455.  * or do whatever a pick at that field should do.
  456.  * we might also change *fld if we want to change the current cursor location.
  457.  * return 1 if we change a field that invalidates any of the times or
  458.  * to update all related fields.
  459.  */
  460. static
  461. chg_fld (bp, fld)
  462. char *bp;
  463. int *fld;
  464. {
  465.     char buf[NC];
  466.     int deghrs = 0, mins = 0, secs = 0;
  467.     int new = 0;
  468.  
  469.     /* switch on just the row/col portion */
  470.     switch (unpackrc(*fld)) {
  471.     case rcfpack (R_ALTM, C_ALTM, 0):
  472.         if (altmenu_setup() == 0) {
  473.         print_updating();
  474.         alt_erase();
  475.         print_alt(2);
  476.         }
  477.         break;
  478.     case rcfpack (R_JD, C_JDV, 0):
  479.         if (!bp) {
  480.         static char p[] = "Julian Date (or n for Now): ";
  481.         f_prompt (p);
  482.         if (read_line (buf, PW-sizeof(p)) <= 0)
  483.             break;
  484.         bp = buf;
  485.         }
  486.         if (bp[0] == 'n' || bp[0] == 'N')
  487.         time_fromsys (&now);
  488.         else
  489.         mjd = atof(bp) - 2415020L;
  490.         set_t0 (&now);
  491.         new = 1;
  492.         break;
  493.     case rcfpack (R_UD, C_UD, 0):
  494.         if (!bp) {
  495.         static char p[] = "utc date (m/d/y, or year.d, or n for Now): ";
  496.         f_prompt (p);
  497.         if (read_line (buf, PW-sizeof(p)) <= 0)
  498.             break;
  499.         bp = buf;
  500.         }
  501.         if (bp[0] == 'n' || bp[0] == 'N')
  502.         time_fromsys (&now);
  503.         else {
  504.         if (decimal_year(bp)) {
  505.             double y = atof (bp);
  506.             year_mjd (y, &mjd);
  507.         } else {
  508.             double day, newmjd0;
  509.             int month, year;
  510.             mjd_cal (mjd, &month, &day, &year); /* init with now */
  511.             f_sscandate (bp, &month, &day, &year);
  512.             cal_mjd (month, day, year, &newmjd0);
  513.             /* if don't give a fractional part to days
  514.              * then retain current hours.
  515.              */
  516.             if ((long)day == day)
  517.             mjd = newmjd0 + mjd_hr(mjd)/24.0;
  518.             else
  519.             mjd = newmjd0;
  520.         }
  521.         }
  522.         set_t0 (&now);
  523.         new = 1;
  524.         break;
  525.     case rcfpack (R_UT, C_UTV, 0):
  526.         if (!bp) {
  527.         static char p[] = "utc time (h:m:s, or n for Now): ";
  528.         f_prompt (p);
  529.         if (read_line (buf, PW-sizeof(p)) <= 0)
  530.             break;
  531.         bp = buf;
  532.         }
  533.         if (bp[0] == 'n' || bp[0] == 'N')
  534.         time_fromsys (&now);
  535.         else {
  536.         double newutc = (mjd-mjd_day(mjd)) * 24.0;
  537.         f_dec_sexsign (newutc, °hrs, &mins, &secs);
  538.         f_sscansex (bp, °hrs, &mins, &secs);
  539.         sex_dec (deghrs, mins, secs, &newutc);
  540.         mjd = mjd_day(mjd) + newutc/24.0;
  541.         }
  542.         set_t0 (&now);
  543.         new = 1;
  544.         break;
  545.     case rcfpack (R_LD, C_LD, 0):
  546.         if (!bp) {
  547.         static char p[] = "local date (m/d/y, or year.d, n for Now): ";
  548.         f_prompt (p);
  549.         if (read_line (buf, PW-sizeof(p)) <= 0)
  550.             break;
  551.         bp = buf;
  552.         }
  553.         if (bp[0] == 'n' || bp[0] == 'N')
  554.         time_fromsys (&now);
  555.         else {
  556.         if (decimal_year(bp)) {
  557.             double y = atof (bp);
  558.             year_mjd (y, &mjd);
  559.             mjd += tz/24.0;
  560.         } else {
  561.             double day, newlmjd0;
  562.             int month, year;
  563.             mjd_cal (mjd-tz/24.0, &month, &day, &year); /* now */
  564.             f_sscandate (bp, &month, &day, &year);
  565.             cal_mjd (month, day, year, &newlmjd0);
  566.             /* if don't give a fractional part to days
  567.              * then retain current hours.
  568.              */
  569.             if ((long)day == day)
  570.             mjd = newlmjd0 + mjd_hr(mjd-tz/24.0)/24.0;
  571.             else
  572.             mjd = newlmjd0;
  573.             mjd += tz/24.0;
  574.         }
  575.         }
  576.         set_t0 (&now);
  577.         new = 1;
  578.         break;
  579.     case rcfpack (R_LT, C_LT, 0):
  580.         if (!bp) {
  581.         static char p[] = "local time (h:m:s, or n for Now): ";
  582.         f_prompt (p);
  583.         if (read_line (buf, PW-sizeof(p)) <= 0)
  584.             break;
  585.         bp = buf;
  586.         }
  587.         if (bp[0] == 'n' || bp[0] == 'N')
  588.         time_fromsys (&now);
  589.         else {
  590.         double newlt = (mjd-mjd_day(mjd)) * 24.0 - tz;
  591.         range (&newlt, 24.0);
  592.         f_dec_sexsign (newlt, °hrs, &mins, &secs);
  593.         f_sscansex (bp, °hrs, &mins, &secs);
  594.         sex_dec (deghrs, mins, secs, &newlt);
  595.         mjd = mjd_day(mjd-tz/24.0) + (newlt + tz)/24.0;
  596.         }
  597.         set_t0 (&now);
  598.         new = 1;
  599.         break;
  600.     case rcfpack (R_LST, C_LSTV, 0):
  601.         if (!bp) {
  602.         static char p[] = "local sidereal time (h:m:s, or n for Now): ";
  603.         f_prompt (p);
  604.         if (read_line (buf, PW-sizeof(p)) <= 0)
  605.             break;
  606.         bp = buf;
  607.         }
  608.         if (bp[0] == 'n' || bp[0] == 'N')
  609.         time_fromsys (&now);
  610.         else {
  611.         double lst, utc;
  612.         now_lst (&now, &lst);
  613.         f_dec_sexsign (lst, °hrs, &mins, &secs);
  614.         f_sscansex (bp, °hrs, &mins, &secs);
  615.         sex_dec (deghrs, mins, secs, &lst);
  616.         lst -= radhr(lng); /* convert to gst */
  617.         range (&lst, 24.0);
  618.         gst_utc (mjd_day(mjd), lst, &utc);
  619.         mjd = mjd_day(mjd) + utc/24.0;
  620.         }
  621.         set_t0 (&now);
  622.         new = 1;
  623.         break;
  624.     case rcfpack (R_TZN, C_TZN, 0):
  625.         if (!bp) {
  626.         static char p[] = "timezone abbreviation (3 char max): ";
  627.         f_prompt (p);
  628.         if (read_line (buf, 3) <= 0)
  629.             break;
  630.         bp = buf;
  631.         }
  632.         (void) strncpy (tznm, bp, sizeof(tznm)-1);
  633.         new = 1;
  634.         break;
  635.     case rcfpack (R_TZONE, C_TZONEV, 0):
  636.         if (!bp) {
  637.         static char p[] = "hours behind utc: ";
  638.         f_prompt (p);
  639.         if (read_line (buf, PW-sizeof(p)) <= 0)
  640.             break;
  641.         bp = buf;
  642.         }
  643.         f_dec_sexsign (tz, °hrs, &mins, &secs);
  644.         f_sscansex (bp, °hrs, &mins, &secs);
  645.         sex_dec (deghrs, mins, secs, &tz);
  646.         new = 1;
  647.         break;
  648.     case rcfpack (R_LONG, C_LONGV, 0):
  649.         if (!bp) {
  650.         static char p[] = "longitude (+ west) (d:m:s): ";
  651.         f_prompt (p);
  652.         if (read_line (buf, PW-sizeof(p)) <= 0)
  653.             break;
  654.         bp = buf;
  655.         }
  656.         f_dec_sexsign (-raddeg(lng), °hrs, &mins, &secs);
  657.         f_sscansex (bp, °hrs, &mins, &secs);
  658.         sex_dec (deghrs, mins, secs, &lng);
  659.         lng = degrad (-lng);         /* want - radians west */
  660.         new = 1;
  661.         break;
  662.     case rcfpack (R_LAT, C_LATV, 0):
  663.         if (!bp) {
  664.         static char p[] = "latitude (+ north) (d:m:s): ";
  665.         f_prompt (p);
  666.         if (read_line (buf, PW-sizeof(p)) <= 0)
  667.             break;
  668.         bp = buf;
  669.         }
  670.         f_dec_sexsign (raddeg(lat), °hrs, &mins, &secs);
  671.         f_sscansex (bp, °hrs, &mins, &secs);
  672.         sex_dec (deghrs, mins, secs, &lat);
  673.         lat = degrad (lat);
  674.         new = 1;
  675.         break;
  676.     case rcfpack (R_HEIGHT, C_HEIGHTV, 0):
  677.         if (!bp) {
  678.         static char p[] = "height above sea level (ft): ";
  679.         f_prompt (p);
  680.         if (read_line (buf, PW-sizeof(p)) <= 0)
  681.             break;
  682.         bp = buf;
  683.         }
  684.         if (sscanf (bp, "%lf", &height) == 1) {
  685.         height /= 2.093e7; /*convert ft to earth radii above sea level*/
  686.         new = 1;
  687.         }
  688.         break;
  689.     case rcfpack (R_NSTEP, C_NSTEPV, 0):
  690.         if (!bp) {
  691.         static char p[] = "number of steps to run: ";
  692.         f_prompt (p);
  693.         if (read_line (buf, 8) <= 0)
  694.             break;
  695.         bp = buf;
  696.         }
  697.         (void) sscanf (bp, "%d", &nstep);
  698.         print_nstep (0);
  699.         break;
  700.     case rcfpack (R_PAUSE, C_PAUSEV, 0):
  701.         if (!bp) {
  702.         static char p[] = "seconds to pause between steps: ";
  703.         f_prompt (p);
  704.         if (read_line (buf, 8) <= 0)
  705.             break;
  706.         bp = buf;
  707.         }
  708.         (void) sscanf (bp, "%d", &spause);
  709.         print_spause (0);
  710.         break;
  711.     case rcfpack (R_TEMP, C_TEMPV, 0):
  712.         if (!bp) {
  713.         static char p[] = "temperature (deg.F): ";
  714.         f_prompt (p);
  715.         if (read_line (buf, PW-sizeof(p)) <= 0)
  716.             break;
  717.         bp = buf;
  718.         }
  719.         if (sscanf (bp, "%lf", &temp) == 1) {
  720.         temp = 5./9.*(temp - 32.0);    /* want degs C */
  721.         new = 1;
  722.         }
  723.         break;
  724.     case rcfpack (R_PRES, C_PRESV, 0):
  725.         if (!bp) {
  726.         static char p[] =
  727.             "atmos pressure (in. Hg; 0 for no refraction correction): ";
  728.         f_prompt (p);
  729.         if (read_line (buf, PW-sizeof(p)) <= 0)
  730.             break;
  731.         bp = buf;
  732.         }
  733.         if (sscanf (bp, "%lf", &pressure) == 1) {
  734.         pressure *= 33.86;        /* want mBar */
  735.         new = 1;
  736.         }
  737.         break;
  738.     case rcfpack (R_EPOCH, C_EPOCHV, 0):
  739.         if (!bp) {
  740.         static char p[] = "epoch (year, or e for Equinox of Date): ";
  741.         f_prompt (p);
  742.         if (read_line (buf, PW-strlen(p)) <= 0)
  743.             break;
  744.         bp = buf;
  745.         }
  746.         if (bp[0] == 'e' || bp[0] == 'E')
  747.         epoch = EOD;
  748.         else {
  749.         double e;
  750.         e = atof(bp);
  751.         year_mjd (e, &epoch);
  752.         }
  753.         new = 1;
  754.         break;
  755.     case rcfpack (R_STPSZ, C_STPSZV, 0):
  756.         if (!bp) {
  757.         static char p[] =
  758.             "step size increment (h:m:s, or <x>d for x days, or r for RTC): ";
  759.         f_prompt (p);
  760.         if (read_line (buf, PW-sizeof(p)) <= 0)
  761.             break;
  762.         bp = buf;
  763.         }
  764.         if (bp[0] == 'r' || bp[0] == 'R')
  765.         tminc = RTC;
  766.         else {
  767.         int last = strlen (bp) - 1;
  768.         if (bp[last] == 'd') {
  769.             /* ends in d so treat as a number of days */
  770.             double x;
  771.             if (sscanf (bp, "%lf", &x) == 1)
  772.             tminc = x * 24.0;
  773.         } else {
  774.             if (tminc == RTC)
  775.             deghrs = mins = secs = 0;
  776.             else
  777.             f_dec_sexsign (tminc, °hrs, &mins, &secs);
  778.             f_sscansex (bp, °hrs, &mins, &secs);
  779.             sex_dec (deghrs, mins, secs, &tminc);
  780.         }
  781.         }
  782.         print_tminc(0);
  783.         set_t0 (&now);
  784.         break;
  785.     case rcfpack (R_PLOT, C_PLOT, 0):
  786.         plot_setup();
  787.         if (plot_ison())
  788.         new = 1;
  789.         break;
  790.     case rcfpack (R_LISTING, C_LISTING, 0):
  791.         listing_setup();
  792.         if (listing_ison())
  793.         new = 1;
  794.         break;
  795.     case rcfpack (R_WATCH, C_WATCH, 0):
  796.         watch (&now, tminc, oppl);
  797.         /* set new reference time to what watch left it.
  798.          * no need to set new since watch just did a redraw.
  799.          */
  800.         set_t0 (&now);
  801.         break;
  802.     case rcfpack (R_DAWN, C_DAWN, 0):
  803.     case rcfpack (R_DUSK, C_DUSK, 0):
  804.     case rcfpack (R_LON, C_LON, 0):
  805.         if (optwi ^= 1) {
  806.         print_updating();
  807.         mm_twilight (&now, 1);
  808.         } else {
  809.         f_blanks (R_DAWN, C_DAWNV, 5);
  810.         f_blanks (R_DUSK, C_DUSKV, 5);
  811.         f_blanks (R_LON, C_LONV, 5);
  812.         }
  813.         break;
  814.     case rcfpack (R_SRCH, C_SRCH, 0):
  815.         srch_setup();
  816.         if (srch_ison())
  817.         new = 1;
  818.         break;
  819.     case rcfpack (R_SUN, C_OBJ, 0):
  820.         toggle_body (SUN);
  821.         break;
  822.     case rcfpack (R_SUN, C_CONSTEL, 0):
  823.         if (oppl & (1<<SUN))
  824.         constellation_msg (SUN, &now);
  825.         break;
  826.     case rcfpack (R_MOON, C_OBJ, 0):
  827.         toggle_body (MOON);
  828.         break;
  829.     case rcfpack (R_MOON, C_CONSTEL, 0):
  830.         if (oppl & (1<<MOON))
  831.         constellation_msg (MOON, &now);
  832.         break;
  833.     case rcfpack (R_MERCURY, C_OBJ, 0):
  834.         toggle_body (MERCURY);
  835.         break;
  836.     case rcfpack (R_MERCURY, C_CONSTEL, 0):
  837.         if (oppl & (1<<MERCURY))
  838.         constellation_msg (MERCURY, &now);
  839.         break;
  840.     case rcfpack (R_VENUS, C_OBJ, 0):
  841.         toggle_body (VENUS);
  842.         break;
  843.     case rcfpack (R_VENUS, C_CONSTEL, 0):
  844.         if (oppl & (1<<VENUS))
  845.         constellation_msg (VENUS, &now);
  846.         break;
  847.     case rcfpack (R_MARS, C_OBJ, 0):
  848.         toggle_body (MARS);
  849.         break;
  850.     case rcfpack (R_MARS, C_CONSTEL, 0):
  851.         if (oppl & (1<<MARS))
  852.         constellation_msg (MARS, &now);
  853.         break;
  854.     case rcfpack (R_JUPITER, C_OBJ, 0):
  855.         toggle_body (JUPITER);
  856.         break;
  857.     case rcfpack (R_JUPITER, C_CONSTEL, 0):
  858.         if (oppl & (1<<JUPITER))
  859.         constellation_msg (JUPITER, &now);
  860.         break;
  861.     case rcfpack (R_JUPITER, C_XTRA, 0):
  862.         if (oppl & (1<<JUPITER)) {
  863.         print_updating();
  864.         alt_erase();
  865.         altmenu_init (F_MNUJ);
  866.         print_alt (2);
  867.         *fld = rcfpack(R_NSTEP, C_NSTEPV, 0);
  868.         }
  869.         break;
  870.     case rcfpack (R_SATURN, C_OBJ, 0):
  871.         toggle_body (SATURN);
  872.         break;
  873.     case rcfpack (R_SATURN, C_CONSTEL, 0):
  874.         if (oppl & (1<<SATURN))
  875.         constellation_msg (SATURN, &now);
  876.         break;
  877.     case rcfpack (R_URANUS, C_OBJ, 0):
  878.         toggle_body (URANUS);
  879.         break;
  880.     case rcfpack (R_URANUS, C_CONSTEL, 0):
  881.         if (oppl & (1<<URANUS))
  882.         constellation_msg (URANUS, &now);
  883.         break;
  884.     case rcfpack (R_NEPTUNE, C_OBJ, 0):
  885.         toggle_body (NEPTUNE);
  886.         break;
  887.     case rcfpack (R_NEPTUNE, C_CONSTEL, 0):
  888.         if (oppl & (1<<NEPTUNE))
  889.         constellation_msg (NEPTUNE, &now);
  890.         break;
  891.     case rcfpack (R_PLUTO, C_OBJ, 0):
  892.         toggle_body (PLUTO);
  893.         break;
  894.     case rcfpack (R_PLUTO, C_CONSTEL, 0):
  895.         if (oppl & (1<<PLUTO))
  896.         constellation_msg (PLUTO, &now);
  897.         break;
  898.     case rcfpack (R_OBJX, C_OBJ, 0):
  899.         /* this might change which columns are used so erase all when
  900.          * returns and redraw if still on.
  901.          */
  902.         obj_setup (OBJX);
  903.         alt_nobody (OBJX);
  904.         if (obj_ison (OBJX)) {
  905.         oppl |= 1 << OBJX;
  906.         print_updating();
  907.         alt_body (OBJX, 1, &now);
  908.         } else
  909.         oppl &= ~(1 << OBJX);    /* already erased; just clear flag */
  910.         break;
  911.     case rcfpack (R_OBJX, C_CONSTEL, 0):
  912.         if (oppl & (1<<OBJX))
  913.         constellation_msg (OBJX, &now);
  914.         break;
  915.     case rcfpack (R_OBJY, C_OBJ, 0):
  916.         /* this might change which columns are used so erase all when
  917.          * returns and redraw if still on.
  918.          */
  919.         obj_setup (OBJY);
  920.         alt_nobody (OBJY);
  921.         if (obj_ison (OBJY)) {
  922.         oppl |= 1 << OBJY;
  923.         print_updating();
  924.         alt_body (OBJY, 1, &now);
  925.         } else
  926.         oppl &= ~(1 << OBJY);    /* already erased; just clear flag */
  927.         break;
  928.     case rcfpack (R_OBJY, C_CONSTEL, 0):
  929.         if (oppl & (1<<OBJY))
  930.         constellation_msg (OBJY, &now);
  931.         break;
  932.     }
  933.  
  934.     return (new);
  935. }
  936.  
  937. static
  938. print_tminc(force)
  939. int force;
  940. {
  941.     static double last = -123.456;    /* anything unlikely */
  942.  
  943.     if (force || tminc != last) {
  944.         if (tminc == RTC)
  945.         f_string (R_STPSZ, C_STPSZV, " RT CLOCK");
  946.         else if (fabs(tminc) >= 24.0)
  947.         f_double (R_STPSZ, C_STPSZV, "%6.4g dy", tminc/24.0);
  948.         else
  949.         f_signtime (R_STPSZ, C_STPSZV, tminc);
  950.         last = tminc;
  951.     }
  952. }
  953.  
  954. /* print stuff on bottom menu */
  955. static
  956. print_alt (howmuch)
  957. int howmuch;
  958. {
  959.     if (howmuch == 2)
  960.         alt_labels();
  961.     if (alt_menumask() == F_MNUJ)
  962.         altj_display (howmuch, &now);
  963.     else {
  964.         int p;
  965.         for (p = nxtbody(-1); p != -1; p = nxtbody(p))
  966.         if (oppl & (1<<p))
  967.             alt_body (p, howmuch, &now);
  968.     }
  969. }
  970.  
  971. print_updating()
  972. {
  973.     f_prompt ("Updating...");
  974. }
  975.  
  976. static
  977. print_nstep(force)
  978. int force;
  979. {
  980.     static int last;
  981.  
  982.     if (force || nstep != last) {
  983.         char buf[16];
  984.         (void) sprintf (buf, "%8d", nstep);
  985.         f_string (R_NSTEP, C_NSTEPV, buf);
  986.         last = nstep;
  987.     }
  988. }
  989.  
  990. static
  991. print_spause(force)
  992. int force;
  993. {
  994.     static int last;
  995.  
  996.     if (force || spause != last) {
  997.         char buf[16];
  998.         (void) sprintf (buf, "%8d", spause);
  999.         f_string (R_PAUSE, C_PAUSEV, buf);
  1000.         last = spause;
  1001.     }
  1002. }
  1003.  
  1004. /* if not plotting/listing/searching then sleep spause seconds.
  1005.  * if time is being based on the real-time clock, sync on the next
  1006.  *   integral multiple of spause seconds after the minute.
  1007.  * check for keyboard action once each second to let it break out early.
  1008.  */
  1009. slp_sync()
  1010. {
  1011.     extern long time();
  1012.  
  1013.     if (spause > 0 && !plot_ison() && !srch_ison() && !listing_ison()) {
  1014.         int n;
  1015.         if (tminc == RTC) {
  1016.         long t;
  1017.         (void) time (&t);
  1018.         n = spause - (t % spause);
  1019.         } else 
  1020.         n = spause;
  1021.         while (--n >= 0)
  1022.         if (chk_char() == 0)
  1023.             break;
  1024.         else
  1025.             (void) sleep (1);
  1026.     }
  1027. }
  1028.  
  1029. static
  1030. toggle_body (p)
  1031. int p;
  1032. {
  1033.     if ((oppl ^= (1<<p)) & (1<<p)) {
  1034.         print_updating();
  1035.         alt_body (p, 1, &now);
  1036.     } else
  1037.         alt_nobody (p);
  1038. }
  1039.